﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SimpleData.ObjectModel.Types
{
	public struct Number
	{
		internal double? value;
		public static readonly Number Zero = new Number() { value = 0 };
		public static readonly Number Null = new Number();
		public static readonly Number PositiveInfinity = new Number() { value = double.PositiveInfinity };
		public static readonly Number NegativeInfinity = new Number() { value = double.NegativeInfinity };
		public static readonly TypeCode[] NotNumbers =
			new TypeCode[]
			{
				TypeCode.Boolean, 
				TypeCode.Char, 
				TypeCode.DateTime, 
				TypeCode.DBNull,
				TypeCode.Empty,
				TypeCode.Decimal,
				TypeCode.Object,
				TypeCode.String
			};

		public bool IsNaN
		{
			get
			{
				return this.value.HasValue && double.IsNaN( this.value.Value );
			}
		}

		public bool IsPositiveInfinity
		{
			get { return this.value.HasValue && double.IsPositiveInfinity( this.value.Value ); }
		}

		public bool IsNegativeInfinity
		{
			get { return this.value.HasValue && double.IsNegativeInfinity( this.value.Value ); }
		}

		#region [ Methods Overloads ]

		public override int GetHashCode()
		{
			return this.value.GetHashCode();
		}

		public override bool Equals( object obj )
		{
			if ( obj is Number )
				return this.value.Equals( ( ( Number )obj ).value );
			else
				return false;
		}

		public override string ToString()
		{
			if ( this.IsNaN )
				return "NaN";
			else if ( this.value.HasValue )
				return this.value.ToString();
			else
				return String.Empty;
		}

		#endregion

		#region [ Cast Operators Overload ]

		public static implicit operator Number( float f )
		{
			return new Number() { value = f };
		}

		public static implicit operator Number( double d )
		{
			return new Number() { value = d };
		}

		public static implicit operator Number( int i )
		{
			return new Number() { value = i };
		}

		public static implicit operator Number( long l )
		{
			return new Number() { value = l };
		}

		public static explicit operator double( Number n )
		{
			if ( !n.value.HasValue )
				throw new InvalidOperationException();
			return n.value.Value;
		}

		public static explicit operator long( Number n )
		{
			if ( !n.value.HasValue )
				throw new InvalidOperationException();
			return ( long )n.value.Value;
		}

		#endregion

		#region [ Logical Operators Overload ]

		public static bool operator >=( Number x, Number y )
		{
			if ( x.value.HasValue && y.value.HasValue )
				return x.value.Value >= y.value.Value;
			else
				return false;
		}

		public static bool operator <=( Number x, Number y )
		{
			if ( x.value.HasValue && y.value.HasValue )
				return x.value.Value <= y.value.Value;
			else
				return false;
		}

		public static bool operator >( Number x, Number y )
		{
			if ( x.value.HasValue && y.value.HasValue )
				return x.value.Value > y.value.Value;
			else
				return false;
		}

		public static bool operator <( Number x, Number y )
		{
			if ( x.value.HasValue && y.value.HasValue )
				return x.value.Value < y.value.Value;
			else
				return false;
		}

		public static bool operator ==( Number x, Number y )
		{
			if ( x.value.HasValue && y.value.HasValue )
				return x.value == y.value;
			else
				return false;
		}

		public static bool operator !=( Number x, Number y )
		{
			if ( x.value.HasValue && y.value.HasValue )
				return x.value != y.value;
			else
				return false;
		}

		public static bool operator true( Number x )
		{
			return x.value.HasValue && x.value != Zero;
		}

		public static bool operator false( Number x )
		{
			return !x.value.HasValue || x.value == Zero;
		}

		public static bool operator !( Number x )
		{
			return x ? false : true;
		}

		#endregion

		#region [ Arithmetic Operators Overload ]

		public static Number operator +( Number x, Number y )
		{
			return ( Number )( x.value + y.value );
		}

		public static Number operator -( Number x, Number y )
		{
			return ( Number )( x.value - y.value );
		}

		public static Number operator /( Number x, Number y )
		{
			return ( Number )( x.value / y.value );
		}

		public static Number operator *( Number x, Number y )
		{
			return ( Number )( x.value * y.value );
		}

		public static Number operator %( Number x, Number y )
		{
			return ( Number )( x.value % y.value );
		}

		#endregion
	}
}
